package com.example.uploadminio.service;

import io.minio.*;
import io.minio.messages.Part;
import io.minio.messages.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.uploadminio.model.MinioProperties;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class MinioUploader {

    private static final Logger log = LoggerFactory.getLogger(MinioUploader.class);
    public static final long PART_SIZE = 5 * 1024 * 1024; // 5MB

    private MinioClient minioClient;

    @Autowired
    private MinioProperties properties;

    private final ExecutorService executor = Executors.newFixedThreadPool(4); // 并发分块上传线程池

    public MinioUploader() {
    }

    public MinioUploader(MinioProperties properties) {
        this.properties = properties;
        initMinioClient();
    }

    private void initMinioClient() {
        if (minioClient == null && properties != null) {
            this.minioClient = MinioClient.builder()
                    .endpoint(properties.getEndpoint())
                    .credentials(properties.getAccessKey(), properties.getSecretKey())
                    .build();
        }
    }

    private MinioClient getClient() {
        if (minioClient == null) {
            initMinioClient();
        }
        return minioClient;
    }

    private int getMaxRetries() {
        return properties.getMaxRetries() != null ? properties.getMaxRetries() : 3;
    }

    private int getRetryDelaySeconds() {
        return properties.getRetryDelay() != null ? properties.getRetryDelay() : 2;
    }

    /**
     * 确保桶存在，不存在则自动创建
     */
    public void ensureBucket() throws Exception {
        MinioClient client = getClient();
        boolean found = client.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucket()).build());
        if (!found) {
            client.makeBucket(MakeBucketArgs.builder().bucket(properties.getBucket()).build());
            log.info("Bucket '{}' not found, created automatically.", properties.getBucket());
        } else {
            log.info("Bucket '{}' exists.", properties.getBucket());
        }
    }

    /**
     * 上传文件（小文件用普通上传，大文件用并行分块上传）
     */
//    public boolean upload(File file, String objectName) {
//        if (file.length() <= PART_SIZE) {
//            return uploadWithRetry(file, objectName);
//        } else {
//            return uploadLargeFileWithProgress(file, objectName);
//        }
//    }

    /**
     * 小文件上传 + 重试
     */
    public boolean uploadWithRetry(File file, String objectName) {
        int maxRetries = getMaxRetries();
        int baseDelay = getRetryDelaySeconds() * 1000;

        for (int attempt = 1; attempt <= maxRetries; attempt++) {
            try (FileInputStream fis = new FileInputStream(file)) {
                getClient().putObject(
                        PutObjectArgs.builder()
                                .bucket(properties.getBucket())
                                .object(objectName)
                                .stream(fis, file.length(), -1)
                                .build()
                );
                log.info("✅ 文件上传成功: {} (尝试 {}/{})", objectName, attempt, maxRetries);
                return true;
            } catch (Exception e) {
                log.error("❌ 上传失败 (第 {} 次, 共 {} 次): {} - {}", attempt, maxRetries, objectName, e.getMessage());
                if (attempt == maxRetries) return false;
                sleepWithBackoff(baseDelay, attempt);
            }
        }
        return false;
    }

    /**
     * 大文件分块并行上传 + 实时日志进度
     */
    public boolean uploadLargeFileWithProgress(File file, String objectName, UploadProgressListener listener) {
        long fileSize = file.length();
        try (InputStream fis = new FileInputStream(file)) {
            // 使用 ProgressInputStream 包装原始流
            ProgressInputStream progressStream = new ProgressInputStream(fis, fileSize, listener);
            getClient().putObject(
                    PutObjectArgs.builder()
                            .bucket(properties.getBucket())
                            .object(objectName)
                            .stream(progressStream, fileSize, -1)
                            .build()
            );
            log.info("✅ 大文件上传完成: {}", objectName);
            return true;
        } catch (Exception e) {
            log.error("🚨 大文件上传失败: {} - {}", objectName, e.getMessage());
            return false;
        }
    }



    private void sleepWithBackoff(int baseDelay, int attempt) {
        try {
            long delay = baseDelay * attempt;
            log.info("⏳ 等待 {} 毫秒后重试...", delay);
            Thread.sleep(delay);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

    public void setProperties(MinioProperties properties) {
        this.properties = properties;
        this.minioClient = null;
        initMinioClient();
    }

    public MinioProperties getProperties() {
        return this.properties;
    }

    public boolean objectExists(String objectName) {
        try {
            MinioClient client = getClient();
            Iterable<Result<Item>> results = client.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(properties.getBucket())
                            .prefix(objectName)
                            .recursive(false)
                            .build()
            );
            for (Result<Item> r : results) {
                Item item = r.get();
                if (item.objectName().equals(objectName)) return true;
            }
            return false;
        } catch (Exception e) {
            throw new RuntimeException("Failed to check object: " + objectName, e);
        }
    }
}
